package org.penguin.study.bk.basic

import java.net.URLDecoder

/**
 * On the JVM platform, numbers are stored as primitive types: int, double, and so on.
 * Exceptions are cases when you create a nullable number reference such as Int? or use generics.
 *
 * All number types support conversions to other types: Byte, Short, Int, Long, Float, Double, Char
 */
// Program entry point
fun main(args: Array<String>) {
    println("81fc1b2671ec4cce8715d3277e95c171".length)
    numbers()
    floating()
    var table = arrayOf(
        "%b" to "Boolean",
        "%c" to "Character",
        "%d" to "Signed Integer",
        "%e" to "Float in scientific format",
        "%f" to "Float in decimal format",
        "%g" to "Float in either decimal or scientific notation based on value",
        "%h" to "Hashcode of argument",
        "%n" to "Line separator",
        "%o" to "Octal Integer",
        "%s" to "String",
        "%t" to "Date or Time",
        "%x" to "Hexadecimal Integer"
    )
    println("\n%[flags][width][.precision]type")

    var formatTemplate = "%-2s\t%s"
    var func = { pair: Pair<String, String> -> println(formatTemplate.format(pair.first, pair.second)) }
    println("\nFormatting Symbols")
    table.forEach(func)

    // All nullable references to a are actually the same object because of the memory optimization that JVM applies to Integer s between -128 and 127.
    // ===的比较[-128,127]，针对会不同
    var a: Int? = 127 // A boxed Int (java.lang.Integer)
    var b: Int? = 127
    println(a == b) // equals
    println(a === b) // 引用

    val c = 123456789;
    println(c.toChar())

    val x = 10
    val y = 9
    // .. closed range
    if (x in 1..y + 1) {
        println("fits in range")
    }

    // half-open range: does not include 100
    for (i in 1 until 100) {
    }

    for (x in 2..10 step 2) {
    }

    for (x in 10 downTo 1) {
    }

    val z = 0
    if (z !in 1..y + 1) {
        println("$z is out of range")
    }

    // Read-only list
    val list = listOf("a", "b", "c")

    // Read-only map
    val map = mapOf("a" to 1, "b" to 2, "c" to 3, "email" to null)
    println(map["b"])
    // map["b"]=200 // error: No set method providing array access

    for ((k, v) in map) {
        println("$k -> $v")
    }


    val p: String by lazy {
        "Lazy property"
    }
    println(p)

    // If-not-null-else shorthand
    var files: List<String>? = null
    println(files?.size ?: "empty") // if files is null, this prints "empty"

    val emails = emptyList<String>() // might be empty
    val mainEmail = emails.firstOrNull() ?: "默认Email"
    println(mainEmail)

    println(URLDecoder.decode("%2b08%3a00", "UTF-8"))

    var email = map["email"] ?: throw IllegalStateException("Email is missing!")

}

fun floating() {
    var formatTemplate = "%f"

    val eFloat = 2.7182818284f // Float, actual value is 2.7182817
    // 24+8, 6-7
    val minFloat: Float = Float.MIN_VALUE
    val maxFloat: Float = Float.MAX_VALUE
    val floatBitSize = Float.SIZE_BITS
    val floatByteSize = Float.SIZE_BYTES
    val floatNegativeInfinity = Float.NEGATIVE_INFINITY
    val floatPositiveInfinity = Float.POSITIVE_INFINITY
    val floatNaN = Float.NaN
    println(
        "Float(${minFloat.toBigDecimal().toPlainString()} - ${
            maxFloat.toBigDecimal().toPlainString()
        }) -> bits: $floatBitSize, bytes: $floatByteSize, negativeInfinity: $floatNegativeInfinity, positiveInfinity: $floatPositiveInfinity, naN: $floatNaN"
    )

//    // 46位到39位
//    println("Float最小值小数位数: ${"0000000000000000000000000000000000000000000014".length}") // 6位小数
//    println("Float最大数小数位数: ${"340282350000000000000000000000000000000".length}") // 6位小数

    // 53+11, 15-16
    val e = 123.5e10
    val minDouble: Double = Double.MIN_VALUE
    val maxDouble: Double = Double.MAX_VALUE
    val doubleBitSize = Double.SIZE_BITS
    val doubleByteSize = Double.SIZE_BYTES
    val doubleNegativeInfinity = Double.NEGATIVE_INFINITY
    val doublePositiveInfinity = Double.POSITIVE_INFINITY
    val doubleNaN = Double.NaN
    println(
        "Double(${minDouble.toBigDecimal().toPlainString()} - ${
            maxDouble.toBigDecimal().toPlainString()
        }) -> bits: $doubleBitSize, bytes: $doubleByteSize, negativeInfinity: $doubleNegativeInfinity, positiveInfinity: $doublePositiveInfinity, naN: $doubleNaN"
    )

//    // 325位到309位
//    println("Double最小值小数位数: ${"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000049".length}")
//    println("Double最大数小数位数: ${"179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".length}")
    println("Float -> ${formatTemplate.format(minFloat)} - ${formatTemplate.format(maxFloat)}")
    println("Double -> ${formatTemplate.format(minDouble)} - ${formatTemplate.format(maxDouble)}")
}

fun numbers() {

    // Read-only local variables are defined using the keyword val. They can be assigned a value only once.
    // Variables that can be reassigned use the var keyword.
    val minByte: Byte = Byte.MIN_VALUE
    val maxByte: Byte = Byte.MAX_VALUE
    val byteBitSize = Byte.SIZE_BITS
    val byteByteSize = Byte.SIZE_BYTES

    val minShort = Short.MIN_VALUE
    val maxShort: Short = Short.MAX_VALUE
    val shortBitSize = Short.SIZE_BITS
    val shortByteSize = Short.SIZE_BYTES

    // Octal literals are not supported.

    val oneMillion = 1_000_000
    val creditCardNumber = 1234_5678_9012_3456L
    val socialSecurityNumber = 999_99_9999L

    val hexBytes = 0xFF_EC_DE_5E
    val hexadecimals = 0x0F

    val bytes = 0b11010010_01101001_10010100_10010010
    val binaries = 0b00001011

    val minInt: Int = Int.MIN_VALUE
    val maxInt: Int = Int.MAX_VALUE
    val intBitSize = Int.SIZE_BITS
    val intByteSize = Int.SIZE_BYTES
    var nullableInt: Int? = 9999 // A boxed Int (java.lang.Integer)

    val threeBillion = 3000000000 // Long
    val minLong: Long = Long.MIN_VALUE
    val maxLong: Long = Long.MAX_VALUE
    val longBitSize = Long.SIZE_BITS
    val longByteSize = Long.SIZE_BYTES

//    val nullableLong: Long? = nullableInt // nullableInt?.toLong() implicit conversion yields a boxed Long (java.lang.Long)

    println("Byte($minByte - $maxByte) -> bits: $byteBitSize, bytes: $byteByteSize")
    println("Short($minShort - $maxShort) -> bits: $shortBitSize, bytes: $shortByteSize")
    println("Int($minInt - $maxInt) -> bits: $intBitSize, bytes: $intByteSize")
    println("Long($minLong - $maxLong) -> bits: $longBitSize, bytes: $longByteSize")
}

// Java 8+
fun isMathematicalInteger(d: Double): Boolean {
    return StrictMath.rint(d) == d && java.lang.Double.isFinite(d)
}

fun describe(obj: Any): String = when (obj) {
    1 -> "One"
    "Hello" -> "Greeting"
    is Long -> "Long"
    !is String -> "Not a string"
    else -> "Any"
}

fun parseInt(str: String): Int? {
    return str.toIntOrNull()
}

fun getStringLength(obj: Any): Int? {
    // `obj` is automatically cast to `String` on the right-hand side of `&&`
    if (obj is String && obj.length > 0) {
        return obj.length
    }
    return null
}

fun arrayOfMinusOnes(size: Int): IntArray {
    return IntArray(size).apply { fill(-1) }
}